# RESTful API сервис, разработанный в рамках международной олимпиады «ИТ-Планета» 2023, конкурса «Прикладное программирование if...else» от компании SimbirSoft

<b> С данным проектом прошел в финал конкурса, набрав 93 балла (функционал 65/70, архитектура 14/15, качество кода 14/15). </b>

С заданием можете ознакомиться по ссылке: https://disk.yandex.ru/i/kjcDG19neVEV0A.


## Использованные технологии и паттерны

1) Spring Boot
2) Spring Security
3) Spring Web
3) Spring Validator
4) JPA
5) JDBC
6) PostgreSQL
7) Liquibase
8) SPGIST index
9) Docker
10) Specification
11) Java Topology Suite
12) Model Mapper
13) Patterns: Builder, Factory, Proxy

## Steps to Setup

#### 1) Скопируйте приложение:
```
git clone https://github.com/OlegProg2020/animal-chipization.git
```
#### 2) Перейдите в корневую папку проекта.
#### 3) Создайте файл .jar:
```
mvnw.cmd clean package -DskipTests=true
```
#### 4) Создайте Docker image:
```
docker build ./ -t webapi
```
#### 5) Запустите приложение:
```
docker-compose up -d
```
Приложение начнет работать по адресу http://localhost:8080


## API Declarations

### 1) Аутентификация пользователя

#### API 1: Регистрация нового аккаунта

```
POST - /registration

- request
Body {
    "firstName": "string", // Имя пользователя
    "lastName": "string", // Фамилия пользователя
    "email": "string", // Адрес электронной почты
    "password": "string" // Пароль от аккаунта пользователя
}

- response
Body {
    "id": "int", // Идентификатор аккаунта пользователя
    "firstName": "string", // Имя пользователя
    "lastName": "string", // Фамилия пользователя
    "email": "string", // Адрес электронной почты
    "role": "string", // Роль аккаунта пользователя, по умолчанию при регистрации "USER"
}
```

|                                                                                                                                                      Условие                                                                                                                                                      | Статус |
| :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----: |
|                                                                                                                                              Запрос успешно выполнен                                                                                                                                              |  201   |
| firstName = null,<br>firstName = "" или состоит из пробелов,<br>lastName = null,<br>lastName = "" или состоит из пробелов,<br>email = null,<br>email = "" или состоит из пробелов,<br>email аккаунта не валидный,<br>password = null,<br>password = "" или состоит из пробелов<br>Неверные авторизационные данные |  400   |
|                                                                                                                                        Запрос от авторизованного аккаунта                                                                                                                                         |  403   |
|                                                                                                                                       Аккаунт с таким email уже существует                                                                                                                                        |  409   |

### 2) Аккаунт пользователя

#### API 1: Получение информации об аккаунте пользователя

```
GET - /accounts/{accountId}
{accountId}: "int"	// Идентификатор аккаунта пользователя

- request
Body {
  empty
}

- response
Body {
    "id": "int", // Идентификатор аккаунта пользователя
    "firstName": "string", // Имя пользователя
    "lastName": "string", // Фамилия пользователя
    "email": "string" // Адрес электронной почты
    "role": "string", // Роль аккаунта пользователя, по умолчанию при регистрации "USER"
}
```

|                                                        Условие                                                         | Статус |
| :--------------------------------------------------------------------------------------------------------------------: | :----: |
|                                                Запрос успешно выполнен                                                 |  200   |
|                                          accountId = null,<br>accountId <= 0                                           |  400   |
|                                          Запрос от неавторизованного аккаунта                                          |  401   |
| Аккаунт с ролью "USER" или "CHIPPER" пытается получить информацию о чужом аккаунте <br>(даже если такого аккаунта нет) |  403   |
|                        Для аккаунтов с ролями "ADMIN": <br>Аккаунт с таким accountId не найден                         |  404   |

#### API 2: Поиск аккаунтов пользователей по параметрам

```
GET - /accounts/search
  ?firstName={firstName}
  &lastName={lastName}
  &email={email}
  &from={from}
  &size={size}
{firstName}: "string",	// Имя пользователя, может использоваться только часть имени без учета регистра, если null - не учитывается
{lastName}: "string",	// Фамилия пользователя, может использоваться только часть фамилии без учета регистра, если null - не учитывается
{email}: "string",	// Адрес электронной почты, может использоваться только часть адреса электронной почты без учета регистра, если null - не учитывается
{from}: "int"		// Количество элементов, которое необходимо пропустить для формирования страницы с результатами (по умолчанию 0)
{size}: "int"		// Количество элементов на странице (по умолчанию 10)

- request
Body {
  empty
}

- response
Body [
      {
        "id”: "int", // Идентификатор аккаунта пользователя
        "firstName": "string", // Имя пользователя
        "lastName": "string", // Фамилия пользователя
        "email": "string" // Адрес электронной почты
        "role": "string", // Роль аккаунта пользователя, по умолчанию "USER"
      }
]

Результаты поиска сортируются по id аккаунта от наименьшего к наибольшему
```

|                                 Условие                                 | Статус |
| :---------------------------------------------------------------------: | :----: |
|                         Запрос успешно выполнен                         |  200   |
|                         from < 0,<br>size <= 0                          |  400   |
|                  Запрос от неавторизованного аккаунта                   |  401   |
|                       У аккаунта нет роли "ADMIN"                       |  403   |

#### API 3: Добавление аккаунта пользователя

```
POST - /accounts

- request
Body {
    "firstName": "string", // Имя пользователя
    "lastName": "string", // Фамилия пользователя
    "email": "string", // Адрес электронной почты
    "password": "string", // Пароль от аккаунта
    "role": "string", // Роль аккаунта пользователя, доступные значения "ADMIN", "CHIPPER", "USER"
}

- response
Body {
    "id": "int", // Идентификатор аккаунта пользователя
    "firstName": "string", // Новое имя пользователя
    "lastName": "string", // Новая фамилия пользователя
    "email": "string", // Новый адрес электронной почты
    "role": "string", // Роль аккаунта пользователя
}
```

|                                                                                                                                                        Условие                                                                                                                                                        | Статус |
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----: |
|                                                                                                                                                Запрос успешно выполнен                                                                                                                                                |  201   |
| firstName = null,<br>firstName = "" или состоит из пробелов,<br>lastName = null,<br>lastName = "" или состоит из пробелов,<br>email = null,<br>email = "" или состоит из пробелов,<br>email аккаунта не валидный,<br>password = null,<br>password = "" или состоит из пробелов,<br>role != "ADMIN", "CHIPPER", "USER" |  400   |
|                                                                                                                                         Запрос от неавторизованного аккаунта                                                                                                                                          |  401   |
|                                                                                                                                              У аккаунта нет роли "ADMIN"                                                                                                                                              |  403   |
|                                                                                                                                         Аккаунт с таким email уже существует                                                                                                                                          |  409   |

#### API 4: Обновление данных аккаунта пользователя

```
PUT - /accounts/{accountId}
{accountId}: "int"	// Идентификатор аккаунта пользователя

- request
Body {
    "firstName": "string", // Новое имя пользователя
    "lastName": "string", // Новая фамилия пользователя
    "email": "string", // Новый адрес электронной почты
    "password": "string" // Новый пароль от аккаунта
    "role": "string", // Роль аккаунта пользователя, доступные значения "ADMIN", "CHIPPER", "USER"
}

- response
Body {
    "id": "int", // Идентификатор аккаунта пользователя
    "firstName": "string", // Новое имя пользователя
    "lastName": "string", // Новая фамилия пользователя
    "email": "string", // Новый адрес электронной почты
    "role": "string", // Роль аккаунта пользователя
}
```

|                                                                                                                                                                            Условие                                                                                                                                                                            | Статус |
| :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----: |
|                                                                                                                                                                    Запрос успешно выполнен                                                                                                                                                                    |  200   |
| accountId = null,<br>accountId <= 0,<br>firstName = null,<br>firstName = "" или состоит из пробелов,<br>lastName = null,<br>lastName = "" или состоит из пробелов,<br>email = null,<br>email = "" или состоит из пробелов,<br>email аккаунта не валидный,<br>password = null,<br>password = "" или состоит из пробелов,<br>role != "ADMIN", "CHIPPER", "USER" |  400   |
|                                                                                                                                                             Запрос от неавторизованного аккаунта                                                                                                                                                              |  401   |
|                                                                                                                              Для аккаунтов с ролями "CHIPPER" и "USER":<br>Обновление не своего аккаунта. <br>Аккаунт не найден                                                                                                                               |  403   |
|                                                                                                                                                      Для аккаунтов с ролью "ADMIN":<br>Аккаунт не найден                                                                                                                                                      |  404   |
|                                                                                                                                                             Аккаунт с таким email уже существует                                                                                                                                                              |  409   |

#### API 5: Удаление аккаунта пользователя

```
DELETE - /accounts/{accountId}
{accountId}: "int"	// Идентификатор аккаунта пользователя

- request
Body {
  empty
}

- response
Body {
  empty
}
```

|                                             Условие                                              | Статус |
| :----------------------------------------------------------------------------------------------: | :----: |
|                                     Запрос успешно выполнен                                      |  200   |
|                accountId = null,<br>accountId <= 0,<br>Аккаунт связан с животным                 |  400   |
|                               Запрос от неавторизованного аккаунта                               |  401   |
| Для аккаунтов с ролями "CHIPPER" и "USER":<br>Удаление не своего аккаунта. <br>Аккаунт не найден |  403   |
|                       Для аккаунтов с ролью "ADMIN":<br>Аккаунт не найден                        |  404   |

### 3) Точка локации животных

#### API 1: Получение информации о точке локации животных

```
GET - /locations/{pointId}
{pointId}: "long"	// Идентификатор точки локации

- request
Body {
  empty
}

- response
Body {
    "id": "long", // Идентификатор точки локации
    "latitude": "double", // Географическая широта в градусах
    "longitude": "double" // Географическая долгота в градусах
}
```

|                       Условие                       | Статус |
| :-------------------------------------------------: | :----: |
|               Запрос успешно выполнен               |  200   |
|           pointId = null,<br>pointId <= 0           |  400   |
|        Запрос от неавторизованного аккаунта         |  401   |
|      Точка локации с таким pointId не найдена       |  404   |

#### API 2: Добавление точки локации животных

```
POST - /locations

- request
Body {
    "latitude": "double", // Географическая широта в градусах
    "longitude": "double" // Географическая долгота в градусах
}

- response
Body {
    "id": "long", // Идентификатор точки локации
    "latitude": "double", // Географическая широта в градусах
    "longitude": "double" // Географическая долгота в градусах
}
```

|                                                      Условие                                                       | Статус |
| :----------------------------------------------------------------------------------------------------------------: | :----: |
|                                              Запрос успешно выполнен                                               |  201   |
| latitude = null,<br>latitude < -90,<br>latitude > 90,<br>longitude = null,<br>longitude < -180,<br>longitude > 180 |  400   |
|                                        Запрос от неавторизованного аккаунта                                        |  401   |
|                                     У аккаунта нет роли "ADMIN" или "CHIPPER"                                      |  403   |
|                             Точка локации с такими latitude и longitude уже существует                             |  409   |

#### API 3: Изменение точки локации животных

```
PUT - /locations/{pointId}
{pointId}: "long"	// Идентификатор точки локации

- request
Body {
    "latitude": "double", // Новая географическая широта в градусах
    "longitude": "double" // Новая географическая долгота в градусах
}

- response
Body {
    "id": "long", // Идентификатор точки локации
    "latitude": "double", // Новая географическая широта в градусах
    "longitude": "double" // Новая географическая долгота в градусах
}
```

|                                                                                                                         Условие                                                                                                                         | Статус |
| :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----: |
|                                                                                                                 Запрос успешно выполнен                                                                                                                 |  200   |
| pointId = null,<br>pointId <= 0,<br>latitude = null,<br>latitude < -90,<br>latitude > 90,<br>longitude = null,<br>longitude < -180,<br>longitude > 180<br>Если точка используется как точка чипирования или как посещенная точка, то её изменять нельзя |  400   |
|                                                                                                          Запрос от неавторизованного аккаунта                                                                                                           |  401   |
|                                                                                                        У аккаунта нет роли "ADMIN" или "CHIPPER"                                                                                                        |  403   |
|                                                                                                        Точка локации с таким pointId не найдена                                                                                                         |  404   |
|                                                                                               Точка локации с такими latitude и longitude уже существует                                                                                                |  409   |

#### API 4: Удаление точки локации животных

```
DELETE - /locations/{pointId}
{pointId}: "long"	// Идентификатор точки локации

- request
Body {
  empty
}

- response
Body {
  empty
}
```

|                               Условие                                | Статус |
| :------------------------------------------------------------------: | :----: |
|                       Запрос успешно выполнен                        |  200   |
| pointId = null,<br>pointId <= 0,<br>Точка локации связана с животным |  400   |
|                 Запрос от неавторизованного аккаунта                 |  401   |
|                     У аккаунта нет роли "ADMIN"                      |  403   |
|               Точка локации с таким pointId не найдена               |  404   |

### 4) Зоны

#### API 1: Получение информации о зоне

```
GET - /areas/{areaId}
{areaId}: "long"		// Идентификатор зоны

- request
Body {
  empty
}

- response
Body {
    "id": "long", // Идентификатор зоны
    "name": "string", // Название зоны
    "areaPoints": [
        {
            "longitude": "double", // Географическая долгота в градусах
            "latitude": "double" // Географическая широта в градусах
        },
        {
            "longitude": "double", // Географическая долгота в градусах
            "latitude": "double" // Географическая широта в градусах
        },
        {
            "longitude": "double", // Географическая долгота в градусах
            "latitude": "double" // Географическая широта в градусах
        }
    ]
}
```

|                          Условие                           | Статус |
| :--------------------------------------------------------: | :----: |
|                  Запрос успешно выполнен                   |  200   |
|               areaId = null,<br>areaId <= 0                |  400   |
|            Запрос от неавторизованного аккаунта            |  401   |
|               Зона с таким areaId не найдена               |  404   |

#### API 2: Добавление зоны

```
POST - /areas

- request
Body {
    "name": "string", // Название зоны
    "areaPoints": [
        {
            "longitude": "double", // Географическая долгота в градусах
            "latitude": "double" // Географическая широта в градусах
        },
        {
            "longitude": "double", // Географическая долгота в градусах
            "latitude": "double" // Географическая широта в градусах
        },
        {
            "longitude": "double", // Географическая долгота в градусах
            "latitude": "double" // Географическая широта в градусах
        }
    ]
}

- response
Body {
    "id": "long", // Идентификатор зоны
    "name": "string", // Название зоны
    "areaPoints": [
        {
            "longitude": "double", // Географическая долгота в градусах
            "latitude": "double" // Географическая широта в градусах
        },
        {
            "longitude": "double", // Географическая долгота в градусах
            "latitude": "double" // Географическая широта в градусах
        },
        {
            "longitude": "double", // Географическая долгота в градусах
            "latitude": "double" // Географическая широта в градусах
        }
    ]
}

ВНИМАНИЕ! Координаты точек в массиве указываются в порядке их соединения для образования замкнутого САМО НЕПЕРЕСЕКАЮЩЕГОСЯ многоугольника, то есть точки соединяются последовательно от первой к последней, последняя соединяется с первой, при этом полученные отрезки не должны пересекать друг друга
```

|                                                                                                                                                                                                                                                                                                                                                         Условие                                                                                                                                                                                                                                                                                                                                                         | Статус |
| :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----: |
|                                                                                                                                                                                                                                                                                                                                                 Запрос успешно выполнен                                                                                                                                                                                                                                                                                                                                                 |  201   |
| name = null,<br>name = "" или состоит из пробелов,<br>areaPoints = null,<br>Одна или несколько точек из areaPoints = null,<br>longitude = null,<br>longitude < -180,<br>longitude > 180,<br>latitude = null,<br>latitude < -90,<br>latitude > 90,<br>areaPoints содержит меньше трех точек.<br>Все точки лежат на одной прямой.<br>Границы новой зоны пересекаются между собой.<br>Границы новой зоны пересекают границы уже существующей зоны.<br>Граница новой зоны находятся внутри границ существующей зоны.<br>Границы существующей зоны находятся внутри границ новой зоны.<br>Новая зона имеет дубликаты точек.<br>Новая зона состоит из части точек существующей зоны и находится на площади существующей зоны. |  400   |
|                                                                                                                                                                                                                                                                                                                                          Запрос от неавторизованного аккаунта                                                                                                                                                                                                                                                                                                                                           |  401   |
|                                                                                                                                                                                                                                                                                                                                               У аккаунта нет роли "ADMIN"                                                                                                                                                                                                                                                                                                                                               |  403   |
|                                                                                                                                                                                                                                                                           Зона, состоящая из таких точек, уже существует. (При этом важен порядок, в котором указаны точки, но не важна начальная точка).<br>Зона с таким name уже существует                                                                                                                                                                                                                                                                           |  409   |

#### API 3: Обновление зоны

```
PUT - /areas/{areaId}
{areaId}: "long"		// Идентификатор зоны

- request
Body {
    "name": "string", // Название зоны
    "areaPoints": [
        {
            "longitude": "double", // Географическая долгота в градусах
            "latitude": "double" // Географическая широта в градусах
        },
        {
            "longitude": "double", // Географическая долгота в градусах
            "latitude": "double" // Географическая широта в градусах
        },
        {
            "longitude": "double", // Географическая долгота в градусах
            "latitude": "double" // Географическая широта в градусах
        }
    ]
}

- response
Body {
    "id": "long", // Идентификатор зоны
    "name": "string" // Название зоны
    "areaPoints": [
        {
            "longitude": "double", // Географическая долгота в градусах
            "latitude": "double" // Географическая широта в градусах
        },
        {
            "longitude": "double", // Географическая долгота в градусах
            "latitude": "double" // Географическая широта в градусах
        },
        {
            "longitude": "double", // Географическая долгота в градусах
            "latitude": "double" // Географическая широта в градусах
        }
    ]
}

ВНИМАНИЕ! Координаты точек в массиве указываются в порядке их соединения для образования замкнутого САМО НЕПЕРЕСЕКАЮЩЕГОСЯ многоугольника, то есть точки соединяются последовательно от первой к последней, последняя соединяется с первой, при этом полученные отрезки не должны пересекать друг друга
```

|                                                                                                                                                                                                                                                                                                                                                                         Условие                                                                                                                                                                                                                                                                                                                                                                          | Статус |
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----: |
|                                                                                                                                                                                                                                                                                                                                                                 Запрос успешно выполнен                                                                                                                                                                                                                                                                                                                                                                  |  200   |
| areaId = null,<br>areaId <= 0,<br>name = null,<br>name = "" или состоит из пробелов,<br>areaPoints = null,<br>Одна или несколько точек из areaPoints = null,<br>longitude = null,<br>longitude < -180,<br>longitude > 180,<br>latitude = null,<br>latitude < -90,<br>latitude > 90<br>areaPoints содержит меньше трех точек.<br>Все точки лежат на одной прямой.<br>Границы новой зоны пересекаются между собой.<br>Границы новой зоны пересекают границы уже существующей зоны.<br>Новая зона имеет дубликаты точек.<br>Граница новой зоны находятся внутри границ существующей зоны.<br>Границы существующей зоны находятся внутри границ новой зоны.<br>Новая зона состоит из части точек существующей зоны и находится на площади существующей зоны. |  400   |
|                                                                                                                                                                                                                                                                                                                                                           Запрос от неавторизованного аккаунта                                                                                                                                                                                                                                                                                                                                                           |  401   |
|                                                                                                                                                                                                                                                                                                                                                               У аккаунта нет роли "ADMIN"                                                                                                                                                                                                                                                                                                                                                                |  403   |
|                                                                                                                                                                                                                                                                                           Зона, состоящая из таких точек, уже существует. (При этом важен порядок, в котором указаны точки, но не важна начальная точка).<br>Зона с таким name уже существует                                                                                                                                                                                                                                                                                            |  409   |

#### API 4: Удаление зоны

```
DELETE - /areas/{areaId}
{areaId}: "long"		// Идентификатор зоны

- request
Body {
  empty
}

- response
Body {
  empty
}
```

|                          Условие                           | Статус |
| :--------------------------------------------------------: | :----: |
|                  Запрос успешно выполнен                   |  200   |
|               areaId = null,<br>areaId <= 0                |  400   |
|            Запрос от неавторизованного аккаунта            |  401   |
|                У аккаунта нет роли "ADMIN"                 |  403   |
|               Зона с таким areaId не найдена               |  404   |

### 5) Типы животных

#### API 1: Получение информации о типе животного

```
GET - /animals/types/{typeId}
{typeId}: "long"		// Идентификатор типа животного

- request
Body {
  empty
}

- response
Body {
    "id": "long", // Идентификатор типа животного
    "type": "string" // Тип животного
}
```

|                          Условие                           | Статус |
| :--------------------------------------------------------: | :----: |
|                  Запрос успешно выполнен                   |  200   |
|               typeId = null,<br>typeId <= 0                |  400   |
|            Запрос от неавторизованного аккаунта            |  401   |
|           Тип животного с таким typeId не найден           |  404   |

#### API 2: Добавление типа животного

```
POST - /animals/types

- request
Body {
    "type": "string"		// Тип животного
}

- response
Body {
    "id": "long",		// Идентификатор типа животного
    "type": "string"		// Тип животного
}
```

|                          Условие                           | Статус |
| :--------------------------------------------------------: | :----: |
|                  Запрос успешно выполнен                   |  201   |
|     type = null,<br>type = "" или состоит из пробелов      |  400   |
|            Запрос от неавторизованного аккаунта            |  401   |
|         У аккаунта нет роли "ADMIN" или "CHIPPER"          |  403   |
|         Тип животного с таким type уже существует          |  409   |

#### API 3: Изменение типа животного

```
PUT - /animals/types/{typeId}
{typeId}: "long"		// Идентификатор типа животного

- request
Body {
    "type": "string"		// Новый тип животного
}

- response
Body {
		"id": "long",		// Идентификатор типа животного
    "type": "string"		// Новый тип животного
}
```

|                                       Условие                                       | Статус |
| :---------------------------------------------------------------------------------: | :----: |
|                               Запрос успешно выполнен                               |  200   |
| typeId = null,<br>typeId <= 0,<br>type = null,<br>type = "" или состоит из пробелов |  400   |
|                        Запрос от неавторизованного аккаунта                         |  401   |
|                      У аккаунта нет роли "ADMIN" или "CHIPPER"                      |  403   |
|                       Тип животного с таким typeId не найден                        |  404   |
|                      Тип животного с таким type уже существует                      |  409   |

#### API 4: Удаление типа животного

```
DELETE - /animals/types/{typeId}
{typeId}: "long"		// Идентификатор типа животного

- request
Body {
empty
}

- response
Body {
  empty
}
```

|                             Условие                             | Статус |
| :-------------------------------------------------------------: | :----: |
|                     Запрос успешно выполнен                     |  200   |
| typeId = null,<br>typeId <= 0<br>Есть животные с типом с typeId |  400   |
|              Запрос от неавторизованного аккаунта               |  401   |
|                   У аккаунта нет роли "ADMIN"                   |  403   |
|             Тип животного с таким typeId не найден              |  404   |

### 6) Животное

#### API 1: Получение информации о животном

```
GET - /animals/{animalId}
{animalId}: "long"	// Идентификатор животного

- request
Body {
  empty
}

- response
Body {
    "id": "long", // Идентификатор животного
    "animalTypes": "[long]", // Массив идентификаторов типов животного
    "weight": "float", // Масса животного, кг
    "length": "float", // Длина животного, м
    "height": "float", // Высота животного, м
    "gender": "string", // Гендерный признак животного, доступные значения “MALE”, “FEMALE”, “OTHER”
    "lifeStatus": "string", // Жизненный статус животного, доступные значения “ALIVE”(устанавливается автоматически при добавлении нового животного), “DEAD”(можно установить при обновлении информации о животном)
    "chippingDateTime": "dateTime", // Дата и время чипирования в формате ISO-8601 (устанавливается автоматически на момент добавления животного)
    "chipperId": "int", // Идентификатор аккаунта с ролью "CHIPPER" или "ADMIN"
    "chippingLocationId": "long", // Идентификатор точки локации животных
    "visitedLocations": "[long]", // Массив идентификаторов объектов с информацией о посещенных точках локаций
    "deathDateTime": "dateTime" // Дата и время смерти животного в формате ISO-8601 (устанавливается автоматически при смене lifeStatus на “DEAD”). Равняется null, пока lifeStatus = “ALIVE”.
}
```

|                  Условие                  | Статус |
| :---------------------------------------: | :----: |
|          Запрос успешно выполнен          |  200   |
|     animalId = null,<br>animalId <= 0     |  400   |
|   Запрос от неавторизованного аккаунта    |  401   |
|      Животное с animalId не найдено       |  404   |

#### API 2: Поиск животных по параметрам

```
GET - /animals/search?
  startDateTime={startDateTime}
  &endDateTime={endDateTime}
  &chipperId={chipperId}
  &chippingLocationId={chippingLocationId}
  &lifeStatus={lifeStatus}
  &gender={gender}
  &from=0
  &size=10
{startDateTime}: "dateTime",	// Дата и время, не раньше которых произошло чипирование животного в формате ISO-8601, если null - не учитывается
{endDateTime}: "dateTime",	// Дата и время, не позже которых произошло чипирование животного в формате ISO-8601, если null - не учитывается
{chipperId}: "int",		// Идентификатор аккаунта с ролью "CHIPPER" или "ADMIN", если null - не учитывается
{chippingLocationId}: "long",	// Идентификатор локации чипирования животного, если null - не учитывается
{lifeStatus}: "string",		// Жизненный статус животного, если null - не учитывается
{gender}:  "string",		// Гендерная принадлежность животного, если null - не учитывается
{from}: "int",			// Количество элементов, которое необходимо пропустить для формирования страницы с результатами (по умолчанию 0)
{size}: "int",			// Количество элементов на странице (по умолчанию 10)

- request
Body {
  empty
}

- response
Body [
      {
        "id": "long", // Идентификатор животного
        "animalTypes": "[long]", // Массив идентификаторов типов животного
        "weight": "float", // Масса животного, кг
        "length": "float", // Длина животного, м
        "height": "float", // Высота животного, м
        "gender": "string", // Гендерный признак животного, доступные значения “MALE”, “FEMALE”, “OTHER”
        "lifeStatus": "string", // Жизненный статус животного, доступные значения “ALIVE”(устанавливается автоматически при добавлении нового животного), “DEAD”(можно установить при обновлении информации о животном)
        "chippingDateTime": "dateTime", // Дата и время чипирования в формате ISO-8601 (устанавливается автоматически на момент добавления животного)
        "chipperId": "int", // Идентификатор аккаунта с ролью "CHIPPER" или "ADMIN"
        "chippingLocationId": "long", // Идентификатор точки локации животных
        "visitedLocations": "[long]", // Массив идентификаторов объектов с информацией о посещенных точках локаций
        "deathDateTime": "dateTime" // Дата и время смерти животного в формате ISO-8601 (устанавливается автоматически при смене lifeStatus на “DEAD”). Равняется null, пока lifeStatus = “ALIVE”.
      }
]

Результаты поиска сортируются по id животного от наименьшего к наибольшему
```

|                                                                                                              Условие                                                                                                              | Статус |
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----: |
|                                                                                                      Запрос успешно выполнен                                                                                                      |  200   |
| from < 0,<br>size <= 0,<br>startDateTime - не в формате ISO-8601,<br>endDateTime - не в формате ISO-8601,<br>chipperId <= 0,<br>chippingLocationId <= 0,<br>lifeStatus != “ALIVE”, “DEAD”,<br>gender != “MALE”, “FEMALE”, “OTHER” |  400   |
|                                                                                               Запрос от неавторизованного аккаунта                                                                                                |  401   |

#### API 3: Добавление нового животного

```
POST - /animals

- request
Body {
    "animalTypes": "[long]", // Массив идентификаторов типов животного
    "weight": "float", // Масса животного, кг
    "length": "float", // Длина животного, м
    "height": "float", // Высота животного, м
    "gender": "string", // Гендерный признак животного, доступные значения “MALE”, “FEMALE”, “OTHER”
    "chipperId": "int", // Идентификатор аккаунта с ролью "CHIPPER" или "ADMIN"
    "chippingLocationId": "long" // Идентификатор точки локации животных
}

- response
Body {
    "id": "long", // Идентификатор животного
    "animalTypes": "[long]", // Массив идентификаторов типов животного
    "weight": "float", // Масса животного, кг
    "length": "float", // Длина животного, м
    "height": "float", // Высота животного, м
    "gender": "string", // Гендерный признак животного, доступные значения “MALE”, “FEMALE”, “OTHER”
    "lifeStatus": "string", // Жизненный статус животного, доступные значения “ALIVE”(устанавливается автоматически при добавлении нового животного), “DEAD”(можно установить при обновлении информации о животном)
    "chippingDateTime": "dateTime", // Дата и время чипирования в формате ISO-8601 (устанавливается автоматически на момент добавления животного)
    "chipperId": "int", // Идентификатор аккаунта с ролью "CHIPPER" или "ADMIN"
    "chippingLocationId": "long", // Идентификатор точки локации животных
    "visitedLocations": "[long]", // Массив идентификаторов объектов с информацией о посещенных точках локаций
    "deathDateTime": "dateTime" // Дата и время смерти животного в формате ISO-8601 (устанавливается автоматически при смене lifeStatus на “DEAD”). Равняется null, пока lifeStatus = “ALIVE”.
}
```

|                                                                                                                                                                                       Условие                                                                                                                                                                                        | Статус |
| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----: |
|                                                                                                                                                                               Запрос успешно выполнен                                                                                                                                                                                |  201   |
| animalTypes = null,<br>animalTypes.size() <= 0<br>Элемент массива animalTypes = null<br>Элемент массива animalTypes <= 0<br>weight = null,<br>weight <=0,<br>length = null,<br>length <=0,<br>height = null,<br>height <=0,<br>gender = null,<br>gender != "MALE", "FEMALE", "OTHER",<br>chipperId = null,<br>chipperId <=0,<br>chippingLocationId = null,<br>chippingLocationId <=0 |  400   |
|                                                                                                                                                                         Запрос от неавторизованного аккаунта                                                                                                                                                                         |  401   |
|                                                                                                                                                                      У аккаунта нет роли "ADMIN" или "CHIPPER"                                                                                                                                                                       |  403   |
|                                                                                                                                     Тип животного не найден,<br>Аккаунт с chipperId не найден,<br>Точка локации с chippingLocationId не найдена                                                                                                                                      |  404   |

#### API 4: Обновление информации о животном

```
PUT - /animals/{animalId}
{animalId}: "long"	// Идентификатор животного

- request
Body {
    "weight": "float", // Масса животного, кг
    "length": "float", // Длина животного, м
    "height": "float", // Высота животного, м
    "gender": "string", // Гендерный признак животного, доступные значения “MALE”, “FEMALE”, “OTHER”
    "lifeStatus": "string", // Жизненный статус животного, доступные значения “ALIVE”(устанавливается автоматически при добавлении нового животного), “DEAD”(можно установить при обновлении информации о животном)
    "chipperId": "int", // Идентификатор аккаунта с ролью "CHIPPER" или "ADMIN"
    "chippingLocationId": "long" // Идентификатор точки локации животных
}

- response
Body {
    "id": "long", // Идентификатор животного
    "animalTypes": "[long]", // Массив идентификаторов типов животного
    "weight": "float", // Масса животного, кг
    "length": "float", // Длина животного, м
    "height": "float", // Высота животного, м
    "gender": "string", // Гендерный признак животного, доступные значения “MALE”, “FEMALE”, “OTHER”
    "lifeStatus": "string", // Жизненный статус животного, доступные значения “ALIVE”(устанавливается автоматически при добавлении нового животного), “DEAD”(можно установить при обновлении информации о животном)
    "chippingDateTime": "dateTime", // Дата и время чипирования в формате ISO-8601 (устанавливается автоматически на момент добавления животного)
    "chipperId": "int", // Идентификатор аккаунта с ролью "CHIPPER" или "ADMIN"
    "chippingLocationId": "long", // Идентификатор точки локации животных
    "visitedLocations": "[long]", // Массив идентификаторов объектов с информацией о посещенных точках локаций
    "deathDateTime": "dateTime" // Дата и время смерти животного в формате ISO-8601 (устанавливается автоматически при смене lifeStatus на “DEAD”). Равняется null, пока lifeStatus = “ALIVE”.
}
```

|                                                                                                                                                                                                                            Условие                                                                                                                                                                                                                             | Статус |
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----: |
|                                                                                                                                                                                                                    Запрос успешно выполнен                                                                                                                                                                                                                     |  200   |
| animalId = null,<br>animalId <=0,<br>weight = null<br>weight <=0,<br>length = null<br>length <=0,<br>height = null<br>height <=0,<br>gender != “MALE”, “FEMALE”, “OTHER”,<br>lifeStatus != “ALIVE”, “DEAD”,<br>chipperId = null,<br>chipperId <=0,<br>chippingLocationId = null,<br>chippingLocationId <=0<br>Установка lifeStatus = “ALIVE”, если у животного <br>lifeStatus = “DEAD”<br>Новая точка чипирования совпадает с первой посещенной точкой локации |  400   |
|                                                                                                                                                                                                              Запрос от неавторизованного аккаунта                                                                                                                                                                                                              |  401   |
|                                                                                                                                                                                                           У аккаунта нет роли "ADMIN" или "CHIPPER"                                                                                                                                                                                                            |  403   |
|                                                                                                                                                                        Животное с animalId не найдено<br>Аккаунт с chipperId не найден<br>Точка локации с chippingLocationId не найдена                                                                                                                                                                        |  404   |

#### API 5: Удаление животного

```
DELETE - /animals/{animalId}
{animalId}: "long"	// Идентификатор животного

- request
Body {
  empty
}

- response
Body {
  empty
}
```

|                                                       Условие                                                       | Статус |
| :-----------------------------------------------------------------------------------------------------------------: | :----: |
|                                               Запрос успешно выполнен                                               |  200   |
| animalId = null,<br>animalId <=0<br>Животное покинуло локацию чипирования, при этом<br>есть другие посещенные точки |  400   |
|                                        Запрос от неавторизованного аккаунта                                         |  401   |
|                                             У аккаунта нет роли "ADMIN"                                             |  403   |
|                                           Животное с animalId не найдено                                            |  404   |

#### API 6: Добавление типа животного к животному

```
POST - /animals/{animalId}/types/{typeId}
{animalId}: "long"	// Идентификатор животного
{typeId}: "long"		// Идентификатор типа животного

- request
Body {
  empty
}

- response
Body {
    "id": "long", // Идентификатор животного
    "animalTypes": "[long]", // Массив идентификаторов типов животного
    "weight": "float", // Масса животного, кг
    "length": "float", // Длина животного, м
    "height": "float", // Высота животного, м
    "gender": "string", // Гендерный признак животного, доступные значения “MALE”, “FEMALE”, “OTHER”
    "lifeStatus": "string", // Жизненный статус животного, доступные значения “ALIVE”(устанавливается автоматически при добавлении нового животного), “DEAD”(можно установить при обновлении информации о животном)
    "chippingDateTime": "dateTime", // Дата и время чипирования в формате ISO-8601 (устанавливается автоматически на момент добавления животного)
    "chipperId": "int", // Идентификатор аккаунта с ролью "CHIPPER" или "ADMIN"
    "chippingLocationId": "long", // Идентификатор точки локации животных
    "visitedLocations": "[long]", // Массив идентификаторов объектов с информацией о посещенных точках локаций
    "deathDateTime": "dateTime" // Дата и время смерти животного в формате ISO-8601 (устанавливается автоматически при смене lifeStatus на “DEAD”). Равняется null, пока lifeStatus = “ALIVE”.
}
```

|                               Условие                               | Статус |
| :-----------------------------------------------------------------: | :----: |
|                       Запрос успешно выполнен                       |  201   |
| animalId = null,<br>animalId <= 0,<br>typeId = null,<br>typeId <= 0 |  400   |
|                Запрос от неавторизованного аккаунта                 |  401   |
|              У аккаунта нет роли "ADMIN" или "CHIPPER"              |  403   |
| Животное с animalId не найдено<br>Тип животного с typeId не найден  |  404   |

#### API 7: Изменение типа животного у животного

```
PUT - /animals/{animalId}/types
{animalId}: "long"	// Идентификатор животного

- request
Body {
  "oldTypeId": "long", 			// Идентификатор текущего типа животного
  "newTypeId": "long"  			// Идентификатор нового типа животного для замены
}

- response
Body {
    "id": "long", // Идентификатор животного
    "animalTypes": "[long]", // Массив идентификаторов типов животного
    "weight": "float", // Масса животного, кг
    "length": "float", // Длина животного, м
    "height": "float", // Высота животного, м
    "gender": "string", // Гендерный признак животного, доступные значения “MALE”, “FEMALE”, “OTHER”
    "lifeStatus": "string", // Жизненный статус животного, доступные значения “ALIVE”(устанавливается автоматически при добавлении нового животного), “DEAD”(можно установить при обновлении информации о животном)
    "chippingDateTime": "dateTime", // Дата и время чипирования в формате ISO-8601 (устанавливается автоматически на момент добавления животного)
    "chipperId": "int", // Идентификатор аккаунта с ролью "CHIPPER" или "ADMIN"
    "chippingLocationId": "long", // Идентификатор точки локации животных
    "visitedLocations": "[long]", // Массив идентификаторов объектов с информацией о посещенных точках локаций
    "deathDateTime": "dateTime" // Дата и время смерти животного в формате ISO-8601 (устанавливается автоматически при смене lifeStatus на “DEAD”). Равняется null, пока lifeStatus = “ALIVE”.
}
```

|                                                                                Условие                                                                                | Статус |
|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:------:|
|                                                                        Запрос успешно выполнен                                                                        |   200  |
|                           animalId = null,<br>animalId <= 0,<br>oldTypeId = null,<br>oldTypeId <= 0,<br>newTypeId = null,<br>newTypeId <= 0                           |   400  |
|                                                                  Запрос от неавторизованного аккаунта                                                                 |   401  |
|                                                               У аккаунта нет роли "ADMIN" или "CHIPPER"                                                               |   403  |
| Животное с animalId не найдено<br>Тип животного с oldTypeId не найден<br>Тип животного с newTypeId не найден<br>Типа животного с oldTypeId нет у животного с animalId |   404  |
|                        Тип животного с newTypeId уже есть у животного с animalId<br>Животное с animalId уже имеет типы с oldTypeId и newTypeId                        |   409  |

#### API 8: Удаление типа животного у животного

```
DELETE - /animals/{animalId}/types/{typeId}
{animalId}: "long"	// Идентификатор животного
{typeId}: "long"		// Идентификатор типа животного

- request
Body {
    empty
}

- response
Body {
    "id": "long", // Идентификатор животного
    "animalTypes": "[long]", // Массив идентификаторов типов животного
    "weight": "float", // Масса животного, кг
    "length": "float", // Длина животного, м
    "height": "float", // Высота животного, м
    "gender": "string", // Гендерный признак животного, доступные значения “MALE”, “FEMALE”, “OTHER”
    "lifeStatus": "string", // Жизненный статус животного, доступные значения “ALIVE”(устанавливается автоматически при добавлении нового животного), “DEAD”(можно установить при обновлении информации о животном)
    "chippingDateTime": "dateTime", // Дата и время чипирования в формате ISO-8601 (устанавливается автоматически на момент добавления животного)
    "chipperId": "int", // Идентификатор аккаунта с ролью "CHIPPER" или "ADMIN"
    "chippingLocationId": "long", // Идентификатор точки локации животных
    "visitedLocations": "[long]", // Массив идентификаторов объектов с информацией о посещенных точках локаций
    "deathDateTime": "dateTime" // Дата и время смерти животного в формате ISO-8601 (устанавливается автоматически при смене lifeStatus на “DEAD”). Равняется null, пока lifeStatus = “ALIVE”.
}
```

|                                                         Условие                                                         | Статус |
| :---------------------------------------------------------------------------------------------------------------------: | :----: |
|                                                 Запрос успешно выполнен                                                 |  200   |
|  animalId = null,<br>animalId <= 0,<br>typeId = null,<br>typeId <= 0<br>У животного только один тип и это тип с typeId  |  400   |
|                                          Запрос от неавторизованного аккаунта                                           |  401   |
|                                        У аккаунта нет роли "ADMIN" или "CHIPPER"                                        |  403   |
|     Животное с animalId не найдено<br>Тип животного с typeId не найден<br>У животного с animalId нет типа с typeId      |  404   |

### 7) Точка локации, посещенная животным

#### API 1: Просмотр точек локации, посещенных животным

```
GET - /animals/{animalId}/locations
  ?startDateTime=
  &endDateTime=
  &from={from}
  &size={size}
{animalId}: "long"		// Идентификатор животного
{startDateTime}: "dateTime"	// Дата и время, не раньше которых нужно искать посещенные точки локации животных в формате ISO-8601, если null - не учитывается
{endDateTime}: "dateTime"	// Дата и время, не позже которых нужно искать посещенные точки локации животных в формате ISO-8601, если null - не учитывается
{from}: "int"			// Количество элементов, которое необходимо пропустить для формирования страницы с результатами (по умолчанию 0)
{size}: "int"			// Количество элементов на странице (по умолчанию 10)

- request
Body {
    empty
}

- response
Body[
      {
          "id": "long", // Идентификатор объекта с информацией о посещенной точке локации
          "dateTimeOfVisitLocationPoint": "dateTime", // Дата и время посещения животным точки локации в формате ISO-8601
          "locationPointId": "long" // Идентификатор посещенной точки локации
      }
]

Результаты поиска сортируются по дате посещения точки от ранней к поздней
```

|                                                                    Условие                                                                     | Статус |
| :--------------------------------------------------------------------------------------------------------------------------------------------: | :----: |
|                                                            Запрос успешно выполнен                                                             |  200   |
| animalId = null,<br>animalId <= 0,<br>from < 0,<br>size <= 0,<br>startDateTime - не в формате ISO-8601,<br>endDateTime - не в формате ISO-8601 |  400   |
|                                                      Запрос от неавторизованного аккаунта                                                      |  401   |
|                                                         Животное с animalId не найдено                                                         |  404   |

#### API 2: Добавление точки локации, посещенной животным

```
POST - /animals/{animalId}/locations/{pointId}
{animalId}: "long"	// Идентификатор животного
{pointId}: "long"	// Идентификатор точки локации

- request
Body {
    empty
}

- response
Body {
    "id": "long", // Идентификатор объекта с информацией о посещенной точке локации
    "dateTimeOfVisitLocationPoint": "dateTime", // Дата и время посещения животным точки локации в формате ISO-8601
    "locationPointId": "long" // Идентификатор посещенной точки локации
}
```

|                                                                                                                                                  Условие                                                                                                                                                   | Статус |
| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----: |
|                                                                                                                                          Запрос успешно выполнен                                                                                                                                           |  201   |
| animalId = null,<br>animalId <= 0,<br>pointId= null,<br>pointId <= 0,<br>У животного lifeStatus = "DEAD"<br>Животное находится в точке чипирования и никуда не перемещалось, попытка добавить точку локации, равную точке чипирования.<br>Попытка добавить точку локации, в которой уже находится животное |  400   |
|                                                                                                                                    Запрос от неавторизованного аккаунта                                                                                                                                    |  401   |
|                                                                                                                                 У аккаунта нет роли "ADMIN" или "CHIPPER"                                                                                                                                  |  403   |
|                                                                                                                    Животное с animalId не найдено<br>Точка локации с pointId не найдена                                                                                                                    |  404   |

#### API 3: Изменение точки локации, посещенной животным

```
PUT - /animals/{animalId}/locations
{animalId}: "long"	// Идентификатор животного

- request
Body {
    "visitedLocationPointId": "long", // Идентификатор объекта с информацией о посещенной точке локации
    "locationPointId": "long" // Идентификатор посещенной точки локации
}

- response
Body {
    "id": "long", // Идентификатор объекта с информацией о посещенной точке локации
    "dateTimeOfVisitLocationPoint": "dateTime", // Дата и время посещения животным точки локации в формате ISO-8601
    "locationPointId": "long" // Идентификатор посещенной точки локации
}
```

|                                                                                                                                                                      Условие                                                                                                                                                                       | Статус |
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----: |
|                                                                                                                                                              Запрос успешно выполнен                                                                                                                                                               |  200   |
| animalId = null,<br>animalId <= 0,<br>visitedLocationPointId = null,<br>visitedLocationPointId <= 0,<br>locationPointId = null,<br>locationPointId <= 0<br>Обновление первой посещенной точки на точку чипирования<br>Обновление точки на такую же точку<br>Обновление точки локации на точку, совпадающую со следующей и/или с предыдущей точками |  400   |
|                                                                                                                                                        Запрос от неавторизованного аккаунта                                                                                                                                                        |  401   |
|                                                                                                                                                     У аккаунта нет роли "ADMIN" или "CHIPPER"                                                                                                                                                      |  403   |
|                                        Животное с animalId не найдено<br>Объект с информацией о посещенной точке локации<br> с visitedLocationPointId не найден.<br>У животного нет объекта с информацией о посещенной точке локации с visitedLocationPointId.<br>Точка локации с locationPointId не найдена                                       |  404   |

#### API 4: Удаление точки локации, посещенной животным

```
DELETE - /animals/{animalId}/locations/{visitedPointId}
{animalId}: "long"		// Идентификатор животного
{visitedPointId}: "long"		// Идентификатор объекта с информацией о посещенной     точке локации

- request
Body {
    empty
}

- response
Body {
    empty
}
```

|                                                                                              Условие                                                                                               | Статус |
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----: |
|                     Запрос успешно выполнен<br>(Если удаляется первая посещенная точка локации, а вторая точка совпадает с точкой чипирования, то она удаляется автоматически)                     |  200   |
|                                                         animalId = null,<br>animalId <= 0<br>visitedPointId = null,<br>visitedPointId <= 0                                                         |  400   |
|                                                                                Запрос от неавторизованного аккаунта                                                                                |  401   |
|                                                                                    У аккаунта нет роли "ADMIN"                                                                                     |  403   |
| Животное с animalId не найдено<br>Объект с информацией о посещенной точке локации с visitedPointId не найден.<br>У животного нет объекта с информацией о посещенной точке локации с visitedPointId |  404   |

### 8) Аналитика по зонам

#### API 1: Просмотр информации о перемещениях животных в зоне

```
GET - /areas/{areaId}/analytics
  ?startDate=
  &endDate=
{areaId}: "long",	// Идентификатор зоны
{startDate}: "date",	// Дата начала интервала в формате ISO-8601 (pattern "yyyy-MM-dd")
{endDate}: "date"	// Дата конца интервала в формате ISO-8601 (pattern "yyyy-MM-dd")

- request
Body {
    empty
}

- response
Body {
    "totalQuantityAnimals": "long", // Общее количество животных, находящихся в этой зоне в указанный интервал времени
    "totalAnimalsArrived": "long", // Общее количество посещений зоны в указанный интервал времени
    "totalAnimalsGone": "long", // Общее количество выходов из зоны в указанный интервал времени
    "animalsAnalytics": [
        {
            "animalType": "string", // Тип животных
            "animalTypeId": "long", // Идентификатор типа животных
            "quantityAnimals": "long", // Количество животных данного типа, находящихся в этой зоне в указанный интервал времени
            "animalsArrived": "long", // Количество животных данного типа, прибывших в эту зону в указанный интервал времени
            "animalsGone": "long", // Количество животных данного типа, покинувших эту зону в указанный интервал времени
        }
    ]
}

ВНИМАНИЕ! Одно и то же животное может несколько раз посетить и покинуть зону в указанный период. Для каждого животного учитывается только один вход в зону и один выход из нее.
```

|                                                                                              Условие                                                                                               | Статус |
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----: |
|                                                                                      Запрос успешно выполнен                                                                                       |  200   |
|            areaId = null,<br>areaId <= 0,<br>startDate - не в формате ISO-8601 (pattern "yyyy-MM-dd")<br>endDate - не в формате ISO-8601 (pattern "yyyy-MM-dd")<br>startDate >= endDate            |  400   |
|                                                                                Запрос от неавторизованного аккаунта                                                                                |  401   |
|                                                                                      Зона с areaId не найдена                                                                                      |  404   |
